package com.googlecode.horncomb.orm.tool.codegen.support;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.w3c.dom.Document;
import org.xml.sax.SAXParseException;

import com.googlecode.horncomb.orm.tool.codegen.FileConverter;
import com.googlecode.horncomb.orm.tool.codegen.ant.FreemarkerCodeGenerator;

import freemarker.ext.dom.NodeModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateNodeModel;

/**
 * @author Daniel
 */
public class CommonFileProcesser implements FileConverter {

	/**
	 * @see cn.horncomb.tools.codegen.Processer#process(java.io.File,
	 *      java.lang.String, java.io.File,
	 *      cn.horncomb.tools.codegen.ant.CodeGenerator)
	 */
	@SuppressWarnings("unchecked")
	public void process(File baseDir, String inFileStr, File destDir,
			FreemarkerCodeGenerator cg) {
		File outFile = null;
		File inFile = null;
		try {
			// the current input file relative to the baseDir
			inFile = new File(baseDir, inFileStr);
			// the output file relative to basedir
			outFile = new File(destDir, inFileStr.substring(0, inFileStr
					.lastIndexOf('.'))
					+ cg.getExtension());

			// only process files that have changed
			if (!cg.isIncremental()
					|| (inFile.lastModified() > outFile.lastModified()
							|| cg.getTemplateFileLastModified() > outFile
									.lastModified() || cg
							.getProjectFileLastModified() > outFile
							.lastModified())) {
				cg.ensureDirectoryFor(outFile);

				// -- command line status
				cg.log("Input:  " + inFileStr, Project.MSG_INFO);

				if (cg.getProjectTemplate() == null
						&& cg.getProjectFile() != null) {
					Document doc = cg.getBuilder().parse(cg.getProjectFile());
					cg.setProjectTemplate(NodeModel.parse(cg.getProjectFile()));
					cg.setProjectNode(NodeModel.wrap(doc));
				}

				// Build the file DOM
				Document docNode = cg.getBuilder().parse(inFile);

				TemplateModel document = NodeModel.parse(inFile);
				TemplateNodeModel docNodeModel = NodeModel.wrap(docNode);
				HashMap root = new HashMap();
				root.put("document", document);
				cg.insertDefaults(root);

				// Process the template and write out
				// the result as the outFile.
				Writer writer = new BufferedWriter(new OutputStreamWriter(
						new FileOutputStream(outFile), cg.getEncoding()));
				try {
					if (cg.getParsedTemplate() == null) {
						throw new BuildException(
								"No template file specified in build script or in XML file");
					}
					if (cg.getPrepareModel() != null) {
						Map vars = new HashMap();
						vars.put("model", root);
						vars.put("doc", docNode);
						if (cg.getProjectNode() != null) {
							vars.put("project", ((NodeModel) cg
									.getProjectNode()).getNode());
						}
						cg.getPrepareModel().execute(vars);
					}
					freemarker.core.Environment env = cg.getParsedTemplate()
							.createProcessingEnvironment(root, writer);
					env.setCurrentVisitorNode(docNodeModel);
					if (cg.getPrepareEnvironment() != null) {
						Map vars = new HashMap();
						vars.put("env", env);
						vars.put("doc", docNode);
						if (cg.getProjectNode() != null) {
							vars.put("project", ((NodeModel) cg
									.getProjectNode()).getNode());
						}
						cg.getPrepareEnvironment().execute(vars);
					}
					env.process();
					writer.flush();
				} finally {
					writer.close();
				}

				cg.log("Output: " + outFile, Project.MSG_INFO);

			}
		} catch (SAXParseException spe) {
			Throwable rootCause = spe;
			if (spe.getException() != null)
				rootCause = spe.getException();
			cg.log("XML parsing error in " + inFile.getAbsolutePath(),
					Project.MSG_ERR);
			cg.log("Line number " + spe.getLineNumber());
			cg.log("Column number " + spe.getColumnNumber());
			throw new BuildException(rootCause, cg.getLocation());
		} catch (Throwable e) {
			if (outFile != null)
				outFile.delete();
			e.printStackTrace();
			throw new BuildException(e, cg.getLocation());
		}

	}

}
